#pragma once

#include <ios>
#include <string>                          // file pathnames.
#include <boost/cstdint.hpp>               // intmax_t.
#include <boost/iostreams/categories.hpp>  // tags.
#include <boost/iostreams/detail/ios.hpp>  // openmode, seekdir, int types.
#include <boost/iostreams/positioning.hpp>
#include <boost/shared_ptr.hpp>
#include <firebird/detail/config.hpp>

namespace boost { namespace iostreams {
class FIREBIRD_DECL win_file_device {
public:
	typedef void*  handle_type;
	typedef char   char_type;
	struct category	: 
			seekable_device_tag,
			closable_tag
	{ };
	win_file_device() : pimpl_(new impl) { }

	explicit win_file_device(handle_type handle, bool close_on_exit = false)
		: pimpl_(new impl(handle, close_on_exit))
	{ }

	explicit win_file_device( const std::string& path,
		std::ios::openmode mode =
			std::ios::in | std::ios::out,
		std::ios::openmode base_mode =
			std::ios::in | std::ios::out )
		: pimpl_(new impl)
	{ open(path, mode, base_mode); }

	void open( const std::string& path,
		std::ios::openmode =
			std::ios::in | std::ios::out,
		std::ios::openmode base_mode =
			std::ios::in | std::ios::out );
	bool is_open() const { return pimpl_->flags_ != 0; }
	std::streamsize read(char_type* s, std::streamsize n);
	std::streamsize write(const char_type* s, std::streamsize n);
	std::streampos seek(stream_offset off, std::ios::seekdir way);
	void close();
private:
	struct impl {
		impl() : fd_(-1), flags_(0) { }
		impl(int fd, bool close_on_exit)
			: fd_(fd), flags_(0)
		{ if (close_on_exit) flags_ |= impl::close_on_exit; }

		impl(handle_type handle, bool close_on_exit)
			: handle_(handle), flags_(has_handle)
		{ if (close_on_exit) flags_ |= impl::close_on_exit; }

		~impl() {
			if (flags_ & close_on_exit) close_impl(*this);
		}
		enum flags {
			close_on_exit = 1,
			has_handle = 2,
			append = 4
		};
		int          fd_;
		handle_type  handle_;
		int          flags_;
	};
	friend struct impl;

	static void close_impl(impl&);

	boost::shared_ptr<impl> pimpl_;
};

struct win_file_source : private win_file_device {
	typedef void*  handle_type;
	typedef char   char_type;
	struct category : public input_seekable, device_tag, closable_tag { };
	using win_file_device::read;
	using win_file_device::seek;
	using win_file_device::open;
	using win_file_device::is_open;
	using win_file_device::close;
	win_file_source() { }

	explicit win_file_source( handle_type handle,
		bool close_on_exit = false )
		: win_file_device(handle, close_on_exit)
	{ }

	explicit win_file_source( const std::string& path,
		std::ios::openmode m = std::ios::in )
		: win_file_device(path, m & ~std::ios::out, std::ios::in)
	{ }
};

struct win_file_sink : private win_file_device {
	typedef void*  handle_type;
	typedef char   char_type;
	struct category : public output_seekable, device_tag, closable_tag { };
	using win_file_device::write;
	using win_file_device::seek;
	using win_file_device::open;
	using win_file_device::is_open;
	using win_file_device::close;
	win_file_sink() { }

	explicit win_file_sink( handle_type handle,
		bool close_on_exit = false )
		: win_file_device(handle, close_on_exit)
	{ }

	explicit win_file_sink( const std::string& path,
		std::ios::openmode m = std::ios::out )
		: win_file_device(path, m & ~std::ios::in, std::ios::out)
	{ }
};

template <typename T>
std::streamsize size(T& s) {
	std::streampos pos = seek(s, 0, std::ios::cur);
	std::streamsize size = (std::streamsize)(seek(s, 0, std::ios::end));
	seek(s, pos, std::ios::beg);
	return size;
}

} }